﻿/// <copyright file="TabPppoeAcMap.cs" company="not for commercial use">
/// Copyright (c) 6/4/2012 All Right Reserved
/// </copyright>
/// <author>Dimitry Kaplin</author>
/// <date>6/4/2012 1:00 </date>
/// <summary>tab monitoring ac logic</summary>
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using EthernetTools.Protocols.PPPoE;
using EthernetTools.Utils;
using DotRas;
using System.Collections.ObjectModel;
using System.IO;
using System.Threading;

namespace EthernetTools.UI
{
    public partial class TabPppoeAcMap : UserControl
    {
        private bool supressEvents = false;
        private bool interruptProcessPacketAtAll = false;

        AutoResetEvent ContinueProcessPackets = new AutoResetEvent(false);
        
        static System.Threading.Thread ReadPacketsThread;
        static System.Threading.Thread ProcessingPacketsThread;
        static ThreadPriority readingPriority = ThreadPriority.AboveNormal;
        
        System.Threading.Thread DialThread;

        RasDialer Dialer = new RasDialer();
        RasHandle pppoeConnectionHandle = new RasHandle();
        RasPhoneBook CurrentUserPhoneBook = new RasPhoneBook();
        RasConnectionWatcher ConnWatcher = new RasConnectionWatcher();

        public EthernetTools.Utils.Delegates.VoidDelegate InvokationUIDelegate;
        public EthernetTools.Utils.Delegates.VoidDelegate InvokationPacketChangeDelegate;
        public EthernetTools.Utils.Delegates.VoidDelegate InvokationPPPoESessionCapturedDelegate;
        public EthernetTools.Utils.Delegates.VoidDelegate InvokationUpdateTreeViewDelegate;

        public static long packetsRead = 0;
        public static long bytesRead = 0;

        System.Exception lastException = null;

        bool defferedTreeviewUpdateCall = false;
        string MyAcMac = "";

        PppoeProtocolPacket myLastPADI;
        PppoeProtocolPacket current;

        object currentSync = new object();

        byte[] currentPPPoESessionPacket = new byte[0];

        Dictionary<string, AccessConcentrator> AcMap = new Dictionary<string, AccessConcentrator>();
        Dictionary<string, string> Host2Ac = new Dictionary<string, string>();
        Dictionary<string, int> HostKickTimes = new Dictionary<string, int>();

        #region Properties

        public static long ReadKilobytes
        {
            get
            {
                return bytesRead / 1024;
            }
        }

        public static long ReadMegabytes
        {
            get
            {
                return bytesRead / (1024 * 1024);
            }
        }

        public static string ReadSize
        {
            get 
            {
                if (bytesRead > 1024*1024)
                {
                    return ReadMegabytes.ToString("#,# Mb", System.Globalization.CultureInfo.InvariantCulture);
                }
                else if (bytesRead > 1024)
                {
                    return ReadKilobytes.ToString("#,# Kb", System.Globalization.CultureInfo.InvariantCulture);
                }

                return bytesRead.ToString("#,# Bytes", System.Globalization.CultureInfo.InvariantCulture);
            }
        }

        public static ThreadPriority ReadingPriority
        {
            get
            {
                return readingPriority;
            }
            set
            {
                readingPriority = value;
                if (ProcessingPacketsThread != null && ReadPacketsThread != null)
                {
                    MainWindow.LogWriteLine("Old priorities: " + ProcessingPacketsThread.Priority + "," + ReadPacketsThread.Priority, Options.Global.LogLevel.Debug);

                    ProcessingPacketsThread.Priority = readingPriority;

                    ReadPacketsThread.Priority = readingPriority;

                    MainWindow.LogWriteLine("New priorities: " + ProcessingPacketsThread.Priority + "," + ReadPacketsThread.Priority, Options.Global.LogLevel.Debug);
                }
            }
        }
        #endregion

        public TabPppoeAcMap()
        {
            this.Disposed += new EventHandler(TabPppoeAcMap_Disposed);
            InitializeComponent();

            packetsRead = 0;
            bytesRead = 0;

            this.Dock = DockStyle.Fill;

            InvokationUIDelegate = new EthernetTools.Utils.Delegates.VoidDelegate(UpdateUIConrols);

            InvokationPacketChangeDelegate = new EthernetTools.Utils.Delegates.VoidDelegate(CurrentPacketUpdated);

            InvokationPPPoESessionCapturedDelegate = new EthernetTools.Utils.Delegates.VoidDelegate(PppoeSessionCaptured);

            InvokationUpdateTreeViewDelegate = new EthernetTools.Utils.Delegates.VoidDelegate(UpdateTreeview);
        
            Packets.PacketQueue.PacketEnqueued += new Packets.PacketQueue.PacketEnqueuedDelegate(PacketQueue_PacketEnqueued);

            Application.ApplicationExit += new EventHandler(Application_ApplicationExit);

            treeView_ACMAP.NodeMouseClick += new TreeNodeMouseClickEventHandler(treeView_ACMAP_NodeMouseClick);
            treeView_ACMAP.AfterSelect += new TreeViewEventHandler(treeView_ACMAP_AfterSelect);

            treeView_ACMAP.ContextMenuStrip = this.ContextMenuTreeView;

            treeView_ACMAP.ContextMenuStrip.ItemClicked += new ToolStripItemClickedEventHandler(ContextMenuStrip_ItemClicked);

            ContextMenuTreeView.VisibleChanged += new EventHandler(ContextMenuTreeView_VisibleChanged);

        }
        
        #region Event handlers

        #region Events : Tab

        private void TabPppoeAcMap_Load(object sender, EventArgs e)
        {
            ReadPacketsThread = new System.Threading.Thread(new System.Threading.ThreadStart(AsyncPacketRead));
            ReadPacketsThread.Priority = ReadingPriority;

            ProcessingPacketsThread = new System.Threading.Thread(new System.Threading.ThreadStart(AsyncPacketProcess));
            ReadPacketsThread.Priority = ReadingPriority;
            InitConnections();

            LoadPreviousACMap();

            // САМЫМ ПОСЛЕДНИМ ЗАПУСКАЕМ ПОТОК НА ЧТЕНИЕ
            Options.Global.GlobalAbortReadingPackets = false;
            ReadPacketsThread.Start();
        }

        private void TabPppoeAcMap_Disposed(object sender, EventArgs e)
        {
            ConnWatcher.EnableRaisingEvents = false;

            ConnWatcher.Connected -= ConnWatcher_Connected;

            ConnWatcher.Disconnected -= ConnWatcher_Disconnected;

            Packets.PacketQueue.PacketEnqueued -= PacketQueue_PacketEnqueued;

            SaveAcMap();

            StopReadingThread();
        }

        private void TabPppoeAcMap_VisibleChanged(object sender, EventArgs e)
        {
            if (this.Visible && defferedTreeviewUpdateCall)
            {
                UpdateTreeview();
            }
        }
        #endregion

        #region Events : Self

        /// <summary>
        /// Recieved pppoe packet, change global variable current - call this method
        /// </summary>
        void CurrentPacketUpdated()
        {
            bool needToUpdateTV = false;

            lock (currentSync)
            {
                bool ContainsSrc = AcMap.ContainsKey(current.sSource);
                bool ContainsDst = AcMap.ContainsKey(current.sDestination);

                var ACnameTag = current.Tags[PPPoETag.TagType.ACName];
                string acName = "";
                if (ACnameTag != null)
                {
                    acName = ACnameTag.Value;
                }

                // конект нового
                if (current.iType == PppoeProtocolPacket.PPPoEType.PADS)
                {
                    // если хост уже законекчен кудалибо
                    if (Host2Ac.ContainsKey(current.sDestination))
                    {
                        // удаляем его из списка коннектов концентратора, если он там есть
                        if (AcMap.ContainsKey(Host2Ac[current.sDestination]))
                        {
                            // удаляем с концентратора
                            AcMap[Host2Ac[current.sDestination]].Remove(current.Destination);
                            needToUpdateTV = true;
                        }
                    }
                    else
                    {
                        // если не законекчен - создаем связку хост - концентратор
                        Host2Ac.Add(current.sDestination, current.sSource);
                    }

                    // если концентратора нет в карте концентраторов 
                    if (!ContainsSrc)
                    {
                        // добавляем
                        AcMap.Add(current.sSource, new AccessConcentrator(current.Source, acName));
                    }
                    // коннектим на концентратор - хост
                    AcMap[current.sSource].Connect(current.Destination, current.SessionId);
                    needToUpdateTV = true;

                    // может это я законектился?
                    if (ByteUtils.Equals(current.Destination, Network.GetMyMacBytes()))
                    {
                        // мой концентратор - сорц
                        MyAcMac = current.sSource;
                    }
                    else // If not me connected
                    {
                        if (MyAcMac == current.sSource)
                        {
                            // может выкинуть засранца с моего концентратора?
                            CheckAndKickFromMyAc(current.Source, current.Destination, current.SessionId);
                        }
                        if (checkBox_kickEveryone.Checked)
                        {
                            // викинуть нах новых
                            KickNew(current.Source, current.Destination, current.SessionId);
                        }
                    }
                }
                //предложение конекта
                else if (current.iType == PppoeProtocolPacket.PPPoEType.PADO)
                {
                    // если в карте концентраторов нет сорца
                    if (!ContainsSrc)
                    {
                        // добавляем его туда
                        AcMap.Add(current.sSource, new AccessConcentrator(current.Source, acName));
                        needToUpdateTV = true;
                    }
                    // in my network, exist strange AC that sending A LOT OF PADO, so it kill all network...
                    AcMap[current.sSource].PadoInc();

                    if (Options.Global.PadoSpeedLimit > 0)
                    {
                        if (AcMap[current.sSource].PadoSpeed > Options.Global.PadoSpeedLimit)
                        {
                            ++AcMap[current.sSource].SpeedLimitViolationTimes;

                            MainWindow.LogWriteLine("Speed limit! Calming AC! ", Options.Global.LogLevel.Info);

                            DoubleKick(current.Source, current.Destination, new byte[] { 0, 0 });

                            AcMap[current.sSource].PadoReset();
                        }
                    }
                }
                // разрыв. по сути только мне шлют :-(
                else if (current.iType == PppoeProtocolPacket.PPPoEType.PADT)
                {
                    if (!ContainsSrc && ContainsDst)
                    {
                        // host -> ac
                        if (ByteUtils.Equals(current.Source, Network.GetMyMacBytes(MainWindow.BindedAdapterId)))
                        {
                            AcMap[current.sDestination].CountPadt2Me++;
                        }
                        AcMap[current.sDestination].Remove(current.Source, current.SessionId);
                        needToUpdateTV = true;
                    }
                    else if (ContainsSrc && !ContainsDst)
                    {
                        // ac -> host
                        if (ByteUtils.Equals(current.Destination, Network.GetMyMacBytes(MainWindow.BindedAdapterId)))
                        {
                            AcMap[current.sSource].CountPadt2Me++;
                        }
                        AcMap[current.sSource].Remove(current.Destination, current.SessionId);
                        needToUpdateTV = true;
                    }
                    else
                    {
                        // unknown
                    }
                }
                // инициализация, опять же по сути только моя
                else if (current.iType == PppoeProtocolPacket.PPPoEType.PADI)
                {
                    // если я запустил ее
                    if (ByteUtils.Equals(Network.GetMyMacBytes(), current.Source))
                    {
                        // для того чтоб знать уник хотс ид
                        myLastPADI = current;
                        // обработать
                        ResponseMyPadi();
                    }

                }
                if (current.iType == PppoeProtocolPacket.PPPoEType.PADR)
                {
                    if (textBox_preffered_AC.Text != "")
                    {
                        // если я запустил ее
                        if (ByteUtils.Equals(Network.GetMyMacBytes(), current.Source))
                        {
                            if (!current.sDestination.Equals(textBox_preffered_AC.Text, StringComparison.InvariantCultureIgnoreCase))
                            {
                                MainWindow.LogWriteLine("Our PADO did not manage to get ahead real AC PADO! Try again after this connect", Options.Global.LogLevel.Warning);
                            }
                        }
                    }
                }
            }

            if (needToUpdateTV)
            {
                this.treeView_ACMAP.Invoke(InvokationUpdateTreeViewDelegate);
            }
        }

        /// <summary>
        /// Call it when change current session pppoe packet
        /// </summary>
        void PppoeSessionCaptured()
        {
            byte[] dest;
            byte[] src;
            byte[] unIntrestingData;
            byte[] sessionId;

            lock (currentPPPoESessionPacket)
            {
                dest = ByteUtils.RemoveBytes(6, ref currentPPPoESessionPacket);
                src = ByteUtils.RemoveBytes(6, ref currentPPPoESessionPacket);
                unIntrestingData = ByteUtils.RemoveBytes(4, ref currentPPPoESessionPacket);
                sessionId = ByteUtils.RemoveBytes(2, ref currentPPPoESessionPacket);
            }

            // определим кто из них АС (на самом деле определить нельзя просто если один из маков есть в списке ас то клево)
            string sDest = ByteUtils.ByteArrToString(dest);
            string sSrc = ByteUtils.ByteArrToString(src);

            bool srcIsAc = AcMap.ContainsKey(sSrc);
            bool dstIsAc = AcMap.ContainsKey(sDest);

            if (!srcIsAc && !dstIsAc)
            {
                // просто не сможем определить кто есть кто...
                return;
            }

            if (dstIsAc)
            {
                byte[] temp = dest; string sTemp = sDest;
                dest = src; sDest = sSrc;
                src = temp; sSrc = sTemp;
                // поменяли местами для однообразия кода
            }

            bool needToAdd = false;

            // если хост уже законекчен кудалибо
            if (Host2Ac.ContainsKey(sDest))
            {
                // удаляем его из списка коннектов концентратора, если он там есть
                if (AcMap.ContainsKey(Host2Ac[sDest]))
                {
                    // удаляем с концентратора если они не совпадают
                    if (Host2Ac[sDest] != sSrc)
                    {
                        needToAdd = true;
                        AcMap[Host2Ac[sDest]].Remove(src);
                        Host2Ac[sDest] = sSrc;
                    }
                }
            }
            else
            {
                needToAdd = true;
                // если не законекчен - создаем связку хост - концентратор
                Host2Ac.Add(sDest, sSrc);
            }

            if (needToAdd)
            {
                // коннектим на концентратор - хост
                AcMap[sSrc].Connect(dest, sessionId);

                // может это я законектился?
                if (ByteUtils.Equals(dest, Network.GetMyMacBytes()))
                {
                    // мой концентратор - сорц
                    MyAcMac = sSrc;
                }
                else // If not me connected
                {
                    if (MyAcMac == current.sSource)
                    {
                        // может выкинуть засранца с моего концентратора?
                        CheckAndKickFromMyAc(src, dest, sessionId);
                    }
                }

                //UpdateTreeview();
                this.treeView_ACMAP.Invoke(InvokationUpdateTreeViewDelegate);
            }
        }
        
        /// <summary>
        /// Event if packet was enqueued
        /// </summary>
        void PacketQueue_PacketEnqueued()
        {
            if (ProcessingPacketsThread.ThreadState == System.Threading.ThreadState.Unstarted)
            {
                ProcessingPacketsThread.Start();
            }
            else
            {
                ContinueProcessPackets.Set();
            }
        }
           
        /// <summary>
        /// Killing reading thread on application exit
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void Application_ApplicationExit(object sender, EventArgs e)
        {
            StopReadingThread();

            if (Dialer.IsBusy)
            {
                Dialer.DialAsyncCancel();
            }
        }
        #endregion

        #region Events : Controls

        #region Events : Conrtols : Treeview

        private void treeView_ACMAP_KeyDown(object sender, KeyEventArgs e)
        {
            if (supressEvents)
                return;
            
            TreeNode tn = treeView_ACMAP.SelectedNode;

            if (tn == null)
            {
                return;
            }

            if (e.Control && e.KeyCode == Keys.C)
            {
                // AC 
                if (tn.Parent == null)
                {
                    Clipboard.SetText(tn.Text);
                }
                else // HOST
                {
                    PPPoEConnectionInfo pci = (PPPoEConnectionInfo)tn.Tag;
                    if (pci != null)
                    {
                        Clipboard.SetText(ByteUtils.ByteArrToString(pci.HostMac));
                    }
                }
            }
        }

        void treeView_ACMAP_NodeMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            if (supressEvents)
                return;

            treeView_ACMAP.SelectedNode = e.Node;
        }

        void ContextMenuStrip_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            if (supressEvents)
                return;

            TreeNode tn = treeView_ACMAP.SelectedNode;
            if (tn == null)
                return;

            if (e.ClickedItem == toolStripMenuItem_removefromList)
            {
                // if AC node
                if (tn.Parent == null)
                {
                    if (AcMap.ContainsKey(tn.Text))
                    {
                        AcMap.Remove(tn.Text);
                    }
                }
                else // host node
                {
                    if (AcMap.ContainsKey(tn.Parent.Text))
                    {
                        PPPoEConnectionInfo pci = (PPPoEConnectionInfo)tn.Tag;

                        if (pci != null)
                        {
                            AcMap[tn.Parent.Text].Remove(pci.HostMac);
                        }
                    }
                }

                UpdateTreeview();
            }
            else if (e.ClickedItem == toolStripMenuItem_terminateAllConneted)
            {
                string acToTermKey = "";
                // if AC node
                if (tn.Parent == null)
                {
                    acToTermKey = tn.Text;
                }
                else // host node
                {
                    acToTermKey = tn.Parent.Text;
                }

                if (acToTermKey != "")
                {
                    if (AcMap.ContainsKey(acToTermKey))
                    {
                        foreach (PPPoEConnectionInfo pci in AcMap[acToTermKey].Connected)
                        {
                            string sHostMac = ByteUtils.ByteArrToString(pci.HostMac);
                            if (!HostKickTimes.ContainsKey(sHostMac))
                            {
                                HostKickTimes.Add(sHostMac, 0);
                            }

                            ++HostKickTimes[sHostMac];

                            MainWindow.LogWriteLine("Kicking: " + sHostMac + " From:" + acToTermKey, Options.Global.LogLevel.Info);

                            if (!ByteUtils.Equals(pci.HostMac, Network.GetMyMacBytes()))
                            {
                                DoubleKick(pci.AcMac, pci.HostMac, pci.SessionId);
                            }
                        }
                    }
                }

                UpdateTreeview();
            }
            else if (e.ClickedItem == toolStripMenuItem_terminateThisConnected)
            {
                if (tn.Parent != null)
                {
                    if (AcMap.ContainsKey(tn.Parent.Text))
                    {
                        PPPoEConnectionInfo killingCon = (PPPoEConnectionInfo)tn.Tag;
                        if (killingCon != null)
                        {
                            string sHostMac = ByteUtils.ByteArrToString(killingCon.HostMac);
                            if (!HostKickTimes.ContainsKey(sHostMac))
                            {
                                HostKickTimes.Add(sHostMac, 0);
                            }

                            ++HostKickTimes[sHostMac];

                            MainWindow.LogWriteLine("Kicking: " + sHostMac + " From:" + tn.Parent.Text, Options.Global.LogLevel.Info);
                            DoubleKick(killingCon.AcMac, killingCon.HostMac, killingCon.SessionId);
                        }
                    }
                }

                UpdateTreeview();
            }
        }

        void treeView_ACMAP_AfterSelect(object sender, TreeViewEventArgs e)
        {
            if (supressEvents)
                return;

            // if AC node
            if (e.Node.Parent == null)
            {
                if (AcMap.ContainsKey(e.Node.Text))
                {
                    AccessConcentrator selAc = AcMap[e.Node.Text];

                    label_ac_info_text.Text = "MAC: " + ByteUtils.ByteArrToString(selAc.Mac) + Environment.NewLine
                        + "AC Name: " + selAc.AcName + Environment.NewLine
                        + "Connected Sessions:" + selAc.Connected.Count + Environment.NewLine
                        + "Times PADT to Me:" + selAc.CountPadt2Me + Environment.NewLine
                        + "Speed violation Times:" + selAc.SpeedLimitViolationTimes;
                }
            }
            else // host node
            {
                if (AcMap.ContainsKey(e.Node.Parent.Text))
                {
                    PPPoEConnectionInfo pci = (PPPoEConnectionInfo)e.Node.Tag;

                    if (pci != null)
                    {
                        string sHostMac = ByteUtils.ByteArrToString(pci.HostMac);

                        label_host_info_text.Text = "MAC: " + ByteUtils.ByteArrToString(pci.HostMac) + Environment.NewLine
                                                + "Session id: " + ByteUtils.ByteArrToString(pci.SessionId) + Environment.NewLine
                                                + "Connected at: " + DateTime.FromFileTimeUtc(pci.TimeStamp).ToString() + Environment.NewLine
                                                + "Kicked : " + (HostKickTimes.ContainsKey(sHostMac) ? HostKickTimes[sHostMac].ToString() : "N/A") + " times " + Environment.NewLine;
                    }
                }
            }
        }

        /// <summary>
        /// When showing context menu on acmap treeview
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void ContextMenuTreeView_VisibleChanged(object sender, EventArgs e)
        {
            if (supressEvents)
                return;

            if (!ContextMenuTreeView.Visible)
            {
                if (defferedTreeviewUpdateCall)
                {
                    supressEvents = true;

                    TreeNode tn = treeView_ACMAP.SelectedNode;
                    UpdateTreeview();
                    treeView_ACMAP.SelectedNode = tn;

                    supressEvents = false;
                    defferedTreeviewUpdateCall = false;
                }
            }
        }
        #endregion
        

        /// <summary>
        /// If user dont wann use fake mac for padi
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void checkBox_useFakeMacForPADI_CheckedChanged(object sender, EventArgs e)
        {
            if (supressEvents)
                return;

            textBox_fakeMac.Enabled = checkBox_useFakeMacForPADI.Checked;
        }
        
        /// <summary>
        /// Send padi to detect ACs
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button_activeRefresh_Click(object sender, EventArgs e)
        {
            byte[] packet = PppoeProtocolPacket.GetPADIPacket(ByteUtils.ParseBytes(
                checkBox_useFakeMacForPADI.Checked ? textBox_fakeMac.Text : Utils.Network.GetMyMacString(MainWindow.BindedAdapterId)));

            if (packet != null)
            {
                MainWindow.LogWriteLine("ACTIVE DISCOVERY:", Options.Global.LogLevel.Debug);
                MainWindow.DebugLog(packet);

                if (!MainWindow.Instance.RawEtherInstance.DoWrite(packet))
                {
                    MainWindow.LogWriteLine("Failed!", Options.Global.LogLevel.Error);
                }
            }
        }

        /// <summary>
        /// Check box i know what am i doing changed
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void checkBox_priv_mode_CheckedChanged(object sender, EventArgs e)
        {
            if (supressEvents)
                return;

            groupBox_priv_mode.Enabled = checkBox_priv_mode.Checked;
        }

        /// <summary>
        /// PADT to all connected
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button_PADT2All_Click(object sender, EventArgs e)
        {
            if (checkBox_priv_mode.Checked)
            {
                MainWindow.LogWriteLine("Killing all: ", Options.Global.LogLevel.Info);
                foreach (KeyValuePair<string, AccessConcentrator> ac in AcMap)
                {
                    foreach (PPPoEConnectionInfo pci in ac.Value.Connected)
                    {

                        string sHostMac = ByteUtils.ByteArrToString(pci.HostMac);
                        if (!HostKickTimes.ContainsKey(sHostMac))
                        {
                            HostKickTimes.Add(sHostMac, 0);
                        }

                        ++HostKickTimes[sHostMac];

                        MainWindow.LogWriteLine("Killing:" + sHostMac + "  From:" + ac.Key, Options.Global.LogLevel.Info);

                        if (!ByteUtils.Equals(pci.HostMac, Network.GetMyMacBytes()))
                        {
                            DoubleKick(pci.AcMac, pci.HostMac, pci.SessionId);
                        }
                    }
                }
            }
        }
             
        /// <summary>
        /// Clearing preffered AC mac
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button_clear_prefferedAC_Click(object sender, EventArgs e)
        {
            textBox_preffered_AC.Text = "";
        }

        private void connectToThatACToolStripMenuItem_DropDownOpening(object sender, EventArgs e)
        {
            ReadOnlyCollection<RasConnection> rcc = RasConnection.GetActiveConnections();

            
            foreach(ToolStripItem tsi in connectToThatACToolStripMenuItem.DropDownItems)
            {
                tsi.Enabled = true;
                // check if we connected
                foreach (RasConnection rc in rcc)
                {
                    if (rc.EntryName == tsi.Text)
                    {
                        tsi.Enabled = false;
                        break;
                    }
                }
            }
        }
        #endregion

        #endregion

        #region Methods

        #region Methods : AcMap Save Load

        /// <summary>
        /// Save current acmap into file
        /// </summary>
        void SaveAcMap()
        {
            if (Options.Global.LoadPreviousACsOnStart)
            {
                try
                {
                    using (System.IO.FileStream fs = new System.IO.FileStream("acmap.ma", System.IO.FileMode.Create))
                    {
                        BinaryWriter bw = new BinaryWriter(fs);

                        bw.Write(Options.Global.MajorVersion);
                        bw.Write(Options.Global.MinorVersion);
                        bw.Write(Options.Global.SubMinorVersion);

                        foreach (KeyValuePair<string, AccessConcentrator> ac in AcMap)
                        {
                            WriteEntry(bw, ac);
                        }

                        bw.Close();
                        fs.Close();
                    }
                }
                catch (System.Exception ex)
                {
                    MessageBox.Show(this, "Error while trying to write \"acmap.ma\" file. \r\n" + ex.Message,
                        "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        /// <summary>
        /// Writes an acmap entry using binarywriter
        /// </summary>
        /// <param name="bw"></param>
        /// <param name="ac"></param>
        void WriteEntry(BinaryWriter bw, KeyValuePair<string, AccessConcentrator> ac)
        {
            // сначала запишем размер записи
            bw.Write(sizeof(int) + ac.Key.Length
                + sizeof(int) + ac.Value.AcName.Length
                + sizeof(int)
                + sizeof(int) + ac.Value.Mac.Length
                + sizeof(int) // subminor 16+ AC Speed Violation times
                );

            // потом данные
            bw.Write(ac.Key.Length);
            bw.Write(ac.Key.ToCharArray());
            bw.Write(ac.Value.AcName.Length);
            bw.Write(ac.Value.AcName.ToCharArray());
            bw.Write(ac.Value.CountPadt2Me);
            bw.Write(ac.Value.Mac.Length);
            bw.Write(ac.Value.Mac);
            bw.Write(ac.Value.SpeedLimitViolationTimes);
        }

        /// <summary>
        /// Loading from file acmap
        /// </summary>
        void LoadPreviousACMap()
        {
            if (Options.Global.LoadPreviousACsOnStart)
            {
                try
                {
                    using (System.IO.FileStream fs = new System.IO.FileStream("acmap.ma", System.IO.FileMode.Open))
                    {
                        BinaryReader br = new BinaryReader(fs);

                        try
                        {
                            int maj = br.ReadInt32();
                            int min = br.ReadInt32();
                            int smin = br.ReadInt32();

                            bool fileOldVersion;
                            bool fileNewVersion;

                            fileOldVersion = maj < Options.Global.MajorVersion
                                || min < Options.Global.MinorVersion
                                || smin < Options.Global.SubMinorVersion;

                            fileNewVersion = maj > Options.Global.MajorVersion
                                || min > Options.Global.MinorVersion
                                || smin > Options.Global.SubMinorVersion;

                            bool read = true;
                            if (fileNewVersion)
                            {
                                read = false;
                                if (MessageBox.Show(this, "Ac map file are from newer version, possible crash, continue reading?",
                                "Possible crash, continue?", MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question) == DialogResult.Yes)
                                {
                                    read = true;
                                }
                            }

                            while (read)
                            {
                                ReadEntry(br);
                            };
                        }
                        catch (System.IO.EndOfStreamException)
                        {
                            // ok
                        }
                        catch (System.Exception ex)
                        {
                            MessageBox.Show(this, "Error reading saved ac map. \r\n" + ex.Message,
                                "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                        }
                        br.Close();
                        fs.Close();
                    }
                }
                catch (System.IO.FileNotFoundException)
                {
                    // ok
                }
                catch (System.Exception ex)
                {
                    MessageBox.Show(this, "Error opening \"acmap.ma\" file. \r\n" + ex.Message,
                        "Critical error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }

            UpdateTreeview();
        }

        /// <summary>
        /// Reading using binary reader an ACmap entry
        /// </summary>
        /// <param name="br"></param>
        void ReadEntry(BinaryReader br)
        {
            string mac = "";
            string acName = "";
            int CountPadt2Me = 0;
            int SpeedViolationTimes = 0;
            byte[] acMac = new byte[] {0,0,0,0,0,0};
            int entryLen = 0;

            // сколько всего байт в записи
            entryLen = br.ReadInt32();

            if (entryLen > 0)
            {
                // string mac
                int maclen = br.ReadInt32();
                StringBuilder sb = new StringBuilder();
                sb.Append(br.ReadChars(maclen));
                mac = sb.ToString();

                entryLen -= maclen + sizeof(int);
            }

            if (entryLen > 0)
            {
                // ac name
                int acnamelen = br.ReadInt32();
                StringBuilder sb = new StringBuilder();
                sb.Append(br.ReadChars(acnamelen));
                acName = sb.ToString();

                entryLen -= acnamelen + sizeof(int);
            }

            if (entryLen > 0)
            {
                //count padt to me
                CountPadt2Me = br.ReadInt32();
                entryLen -= sizeof(int);
            }

            if (entryLen > 0)
            {
                // byte[] mac
                int bytemaclen = br.ReadInt32();
                acMac = br.ReadBytes(bytemaclen);
                entryLen -= bytemaclen + sizeof(int);
            }
            if (entryLen > 0)
            {
                //speed violation times
                SpeedViolationTimes = br.ReadInt32();
                entryLen -= sizeof(int);
            }

            AccessConcentrator ac = new AccessConcentrator(acMac, acName);
            ac.CountPadt2Me = CountPadt2Me;
            ac.SpeedLimitViolationTimes = SpeedViolationTimes;

            if (!AcMap.ContainsKey(mac))
            {
                AcMap.Add(mac, ac);
            }
        }
        #endregion

        #region Methods : GUI

        /// <summary>
        /// update controls
        /// </summary>
        /// <param name="args"></param>
        void UpdateUIConrols()
        {
            ReadStatusLabel.Text = "In queue: " + Packets.PacketQueue.PacketCount + "; Already read: " + packetsRead + " packets, Bytes: " + bytesRead;
            /*MainWindow.LogWriteLine(ByteUtils.ByteArrToString(new byte[] { currentPacket[0], currentPacket[1], currentPacket[2], currentPacket[3], currentPacket[4], currentPacket[5] })
                     + "   <<<<<-  "
                     + ByteUtils.ByteArrToString(new byte[] { currentPacket[6], currentPacket[7], currentPacket[8], currentPacket[9], currentPacket[10], currentPacket[11] }));
            */
            toolStrip_ImConnectedTo.Text = "I'm connected to:" + MyAcMac;

        }

        /// <summary>
        /// Updating a treeview according acmap dictionary
        /// </summary>
        void UpdateTreeview()
        {
            if (ContextMenuTreeView.Visible)
            {
                defferedTreeviewUpdateCall = true;
                return;
            }
            if (this.Visible == false)
            {
                // why we should update treeview that we doesn't see =)
                defferedTreeviewUpdateCall = true;
                return;
            }

            string stnText = "";

            if (treeView_ACMAP.SelectedNode != null)
            {
                stnText = treeView_ACMAP.SelectedNode.Text;
            }
            treeView_ACMAP.Nodes.Clear();

            foreach (KeyValuePair<string, AccessConcentrator> ac in AcMap)
            {
                TreeNode tn = treeView_ACMAP.Nodes.Add(ac.Key);
                tn.SelectedImageIndex = tn.ImageIndex = 1;
                tn.Tag = ac.Value;

                foreach (PPPoEConnectionInfo pci in ac.Value.Connected)
                {
                    TreeNode tn2 = tn.Nodes.Add(pci.ToString());
                    tn2.SelectedImageIndex = tn2.ImageIndex = 4;
                    tn2.Tag = pci;
                }

                if (tn.Text == stnText)
                {
                    treeView_ACMAP.SelectedNode = tn;
                }
            }
        }

        #endregion

        #region Mathods : PPPoE

        /// <summary>
        /// Here we send pado if preffered ac is choosen
        /// </summary>
        private void ResponseMyPadi()
        {
            if (textBox_preffered_AC.Text != "")
            {
                if (AcMap.ContainsKey(textBox_preffered_AC.Text))
                {
                     byte[] pado= new byte[0];

                    if (myLastPADI.Tags[PPPoETag.TagType.ServiceName] != null
                        && myLastPADI.Tags[PPPoETag.TagType.ServiceName].ByteValue.Length > 0)
                    {
                        // response with specified service name (ISP i guess)
                        pado = PppoeProtocolPacket.GetPADOPacket(
                                myLastPADI.Source,
                                ByteUtils.ParseBytes(textBox_preffered_AC.Text),
                                myLastPADI.Tags[PPPoETag.TagType.HostUniq].ByteValue,
                                ByteUtils.ParseBytes(AcMap[textBox_preffered_AC.Text].AcName, ByteParseStringMethod.String2ByteDirectly),
                                myLastPADI.Tags[PPPoETag.TagType.ServiceName].ByteValue,
                                new byte[] { 1, 1, 0, 0 });
                    }
                    else
                    {
                        // response without specified service name (ISP i guess)
                        pado = PppoeProtocolPacket.GetPADOPacket(
                                myLastPADI.Source,
                                ByteUtils.ParseBytes(textBox_preffered_AC.Text),
                                myLastPADI.Tags[PPPoETag.TagType.HostUniq].ByteValue,
                                ByteUtils.ParseBytes(AcMap[textBox_preffered_AC.Text].AcName, ByteParseStringMethod.String2ByteDirectly),
                                new byte[] {0,0,0,0,0x3c});
                    }
                    if (pado != null)
                    {
                        if (!MainWindow.Instance.RawEtherInstance.DoWrite(pado))
                        {
                            MainWindow.LogWriteLine("Failed!" + ByteUtils.ByteArrToString(pado), Options.Global.LogLevel.Error);
                        }
                    }

                }
                else
                {
                    MainWindow.LogWriteLine("Selected Preffered mac is not in AC tree list! Can't get ac-name, aborting PADO.", Options.Global.LogLevel.Error);
                }
            }
        }

        /// <summary>
        /// Terminate session with writing to log and statistics update
        /// </summary>
        /// <param name="source"></param>
        /// <param name="dest"></param>
        /// <param name="session"></param>
        private void KickNew(byte[] source, byte[] dest, byte[] session)
        {
            string sHostMac = ByteUtils.ByteArrToString(dest);
            if (!HostKickTimes.ContainsKey(sHostMac))
            {
                HostKickTimes.Add(sHostMac, 0);
            }

            ++HostKickTimes[sHostMac];
            MainWindow.LogWriteLine("Kicking : " + sHostMac + "  From:" + ByteUtils.ByteArrToString(source), Options.Global.LogLevel.Info);

            DoubleKick(source, dest, session);
        }

        /// <summary>
        ///  Sync Event on my ac connected
        /// </summary>
        /// <param name="source">Mac of AC</param>
        /// <param name="dest">Mac who connected</param>
        /// <param name="session">Their Session ID</param>
        /// <param name="sSource">String mac of ac</param>
        private void CheckAndKickFromMyAc(byte[] source, byte[] dest, byte[] session)
        {
            if (checkBox_protectMyAC.Checked)
            {

                string sHostMac = ByteUtils.ByteArrToString(dest);
                if (!HostKickTimes.ContainsKey(sHostMac))
                {
                    HostKickTimes.Add(sHostMac, 0);
                }

                ++HostKickTimes[sHostMac];
                MainWindow.LogWriteLine("Kicking : " + sHostMac + "  From:" + ByteUtils.ByteArrToString(source), Options.Global.LogLevel.Info);

                DoubleKick(source, dest, session);
            }

        }

        /// <summary>
        /// Kicking dest from source
        /// </summary>
        /// <param name="source">AC Mac</param>
        /// <param name="dest">Host mac</param>
        /// <param name="session">Session id</param>
        private void DoubleKick(byte[] source, byte[] dest, byte[] session)
        {
            if ((Options.Global.CurrentPadtMethod & (int)Options.Global.PadtMethod.HostNone) == 0)
            {
                byte[] destination = new byte[] { 0, 0, 0, 0, 0, 0 };

                if ((Options.Global.CurrentPadtMethod & (int)Options.Global.PadtMethod.HostDirect) != 0)
                {
                    destination = source;
                }
                else if ((Options.Global.CurrentPadtMethod & (int)Options.Global.PadtMethod.HostBroadcast) != 0)
                {
                    destination = Network.EthBroadCastMac;
                }

                byte[] packet = PppoeProtocolPacket.GetPADTPacket(
                    destination,
                    dest, session);

                if (packet != null)
                {
                    if (!MainWindow.Instance.RawEtherInstance.DoWrite(packet))
                    {
                        MainWindow.LogWriteLine("Failed!" + ByteUtils.ByteArrToString(packet), Options.Global.LogLevel.Error);
                    }
                }
            }

            if ((Options.Global.CurrentPadtMethod & (int)Options.Global.PadtMethod.ConcentratorNone) == 0)
            {
                byte[] destination = new byte[] { 0, 0, 0, 0, 0, 0 };

                if ((Options.Global.CurrentPadtMethod & (int)Options.Global.PadtMethod.ConcentratorDirect) != 0)
                {
                    destination = dest;
                }
                else if ((Options.Global.CurrentPadtMethod & (int)Options.Global.PadtMethod.ConcentratorBroadcat) != 0)
                {
                    destination = Network.EthBroadCastMac;
                }

                byte[] packet = PppoeProtocolPacket.GetPADTPacket(
                    destination,
                    source, session);

                if (packet != null)
                {
                    if (!MainWindow.Instance.RawEtherInstance.DoWrite(packet))
                    {
                        MainWindow.LogWriteLine("Failed!" + ByteUtils.ByteArrToString(packet), Options.Global.LogLevel.Error);
                    }
                }
            }
        }
        #endregion

        #region Mathods : RAS

        private bool CheckPPPoEConnection()
        {
            ReadOnlyCollection<RasConnection> rcc = RasConnection.GetActiveConnections();

            // check if we connected
            foreach (RasConnection rc in rcc)
            {
                if (rc.Device.DeviceType.Equals(RasDeviceType.PPPoE,StringComparison.InvariantCultureIgnoreCase))
                {
                    toolStripPPPoEConnectionStatus.Text = "PPPoE Connection Status: Connected to " + rc.EntryName;
                    return true;
                }
            }
            return false;
        }

        private bool CheckPPPoEConnectionTo(Guid id)
        {
            ReadOnlyCollection<RasConnection> rcc = RasConnection.GetActiveConnections();

            // check if we connected
            foreach (RasConnection rc in rcc)
            {
                if (rc.EntryId == id)
                {
                    toolStripPPPoEConnectionStatus.Text = "PPPoE Connection Status: Connected to " + rc.EntryName;
                    return true;
                }
            }
            return false;
        }

        /// <summary>
        /// Init ras to get connections
        /// </summary>
        private void InitConnections()
        {
            CurrentUserPhoneBook.Open(true);

            ConnWatcher.EnableRaisingEvents = true;

            ConnWatcher.Connected += new EventHandler<RasConnectionEventArgs>(ConnWatcher_Connected);
            ConnWatcher.Disconnected += new EventHandler<RasConnectionEventArgs>(ConnWatcher_Disconnected);

            bool ReIsConnected = false;
            foreach (RasEntry re in CurrentUserPhoneBook.Entries)
            {
                ReIsConnected = false;
                if (re.Device.DeviceType.Equals(RasDeviceType.PPPoE,StringComparison.InvariantCultureIgnoreCase))
                {

                    ReIsConnected = CheckPPPoEConnectionTo(re.Id);

                    // adding listview
                    ListViewItem lvi = new ListViewItem();
                    lvi.Text = re.Name;
                    lvi.Tag = re;
                    lvi.ImageIndex = 6;
                    listView_connections.Items.Add(lvi);


                    ToolStripItem tsi = connectToThatACToolStripMenuItem.DropDownItems.Add(re.Name);
                    tsi.Tag = re;
                    tsi.Enabled = !ReIsConnected;
                    tsi.Click += new EventHandler(ContextMenuConnecToAC_Click);
                }
            }

            listView_connections.DoubleClick += new EventHandler(listView_connections_DoubleClick);


            Dialer.DialCompleted -= Dialer_DialCompleted;
            Dialer.DialCompleted += new EventHandler<DialCompletedEventArgs>(Dialer_DialCompleted);
         }

        void ConnWatcher_Disconnected(object sender, RasConnectionEventArgs e)
        {
            foreach (ToolStripItem tsi in connectToThatACToolStripMenuItem.DropDownItems)
            {
                if (tsi.Text.Equals(e.Connection.EntryName))
                {
                    tsi.Enabled = true;
                    MainWindow.LogWriteLine("enabling " + tsi.Text, Options.Global.LogLevel.Debug);
                    toolStripPPPoEConnectionStatus.Text = "PPPoE Connection Status: Disconnected";   
                    break;
                }
            }
            MainWindow.LogWriteLine(e.Connection.EntryName + " disconnected.", Options.Global.LogLevel.Info);
        }

        void ConnWatcher_Connected(object sender, RasConnectionEventArgs e)
        {
            foreach (ToolStripItem tsi in connectToThatACToolStripMenuItem.DropDownItems)
            {
                if (tsi.Text.Equals(e.Connection.EntryName))
                {
                    tsi.Enabled = false;
                    break;
                }
            }
            MainWindow.LogWriteLine(e.Connection.EntryName + " connected.", Options.Global.LogLevel.Info);
        }

        void ContextMenuConnecToAC_Click(object sender, EventArgs e)
        {
            if (supressEvents)
                return;

            TreeNode tn = treeView_ACMAP.SelectedNode;
            if (tn == null)
                return;

            if (tn.Parent == null)
            {
                textBox_preffered_AC.Text = tn.Text;
            }

            try
            {
                RasEntry re = (RasEntry)((ToolStripItem)sender).Tag;

                re.Update();
                Dial(re);

                toolStripPPPoEConnectionStatus.Text = "PPPoE Connection Status: Connecting to " + re.Name;
            }
            catch (System.Exception ex)
            {
                MainWindow.LogWriteLine(ex.ToString(), Options.Global.LogLevel.Info);
            }
      
        }

        void listView_connections_DoubleClick(object sender, EventArgs e)
        {
            if (supressEvents)
                return;

            if (listView_connections.SelectedItems.Count > 0)
            {
                RasEntry re = (RasEntry)listView_connections.SelectedItems[0].Tag;

                re.Update();
                RasConnection rc = RasConnection.GetActiveConnectionByName(re.Name, CurrentUserPhoneBook.Path, StringComparison.InvariantCultureIgnoreCase);

                if (rc == null)
                {
                    toolStripPPPoEConnectionStatus.Text = "PPPoE Connection Status: Connecting to " + re.Name;           
                    Dial(re);
                }
                else
                {
                    MainWindow.LogWriteLine("Selected Entry already connected!", Options.Global.LogLevel.Error);
                }

            }
        }

        private void Dial(RasEntry re)
        {
            if (Dialer.IsBusy)
            {
                try
                {
                    this.Dialer.DialAsyncCancel();
                }
                catch (System.Exception ex)
                {
                    MainWindow.LogWriteLine("Can't stop previous dialing thread, aborting dial.\r\n" + ex.Message, Options.Global.LogLevel.Error);
                    return;
                }
                
            }

            // This button will be used to dial the connection.
            this.Dialer.EntryName = re.Name;
            this.Dialer.PhoneBookPath = CurrentUserPhoneBook.Path;
            this.Dialer.Timeout = (int)Options.Global.DialTimeOut;

            toolStripProgressBarConnection.Visible = true;
            toolStripProgressBarConnection.Maximum = this.Dialer.Timeout;
            toolStripProgressBarConnection.Value = 0;
            toolStripProgressBarConnection.Step = 100;

            this.Dialer.AllowUseStoredCredentials = true;
            this.Dialer.HangUpPollingInterval = 0;

            this.pppoeConnectionHandle = Dialer.DialAsync();
        }

        void Dialer_DialCompleted(object sender, DialCompletedEventArgs e)
        {
            this.pppoeConnectionHandle = e.Handle;
            if (e.Cancelled)
            {
                lastException = new Exception("Dial was canceled");
                if (!this.IsDisposed || !this.Disposing)
                {
                    this.Invoke(new EthernetTools.Utils.Delegates.VoidDelegate(UiCantConnect));
                }
            }
            else if (e.Connected)
            {
                if (!this.IsDisposed || !this.Disposing)
                {
                    this.Invoke(new EthernetTools.Utils.Delegates.VoidDelegate(UiConnected));
                }
            }

            else if (e.TimedOut)
            {
                lastException = new Exception("Dial was time outed");
                if (!this.IsDisposed || !this.Disposing)
                {
                    this.Invoke(new EthernetTools.Utils.Delegates.VoidDelegate(UiCantConnect));
                }
            }

            if ((this.pppoeConnectionHandle.IsInvalid || this.pppoeConnectionHandle.IsClosed)
                && !e.Cancelled && !e.TimedOut)
            {
                lastException = new Exception("There Were not exceptions while dial, but connection handle is invalid or closed");
                if (!this.IsDisposed || !this.Disposing)
                {
                    this.Invoke(new EthernetTools.Utils.Delegates.VoidDelegate(UiCantConnect));
                }
                return;
            }
        }

        private void UiConnected()
        {
            toolStripPPPoEConnectionStatus.Text = "PPPoE Connection Status: connected to " + Dialer.EntryName;
            toolStripProgressBarConnection.Visible = false;
        }
        private void UiCantConnect()
        {
            toolStripPPPoEConnectionStatus.Text = "PPPoE Connection Status: disconnected";
            if (lastException != null)
            {
                MainWindow.LogWriteLine("Cant connect!" + lastException.Message, Options.Global.LogLevel.Error);
            }

            toolStripProgressBarConnection.Visible = false;

            foreach (ToolStripItem tsi in connectToThatACToolStripMenuItem.DropDownItems)
            {
                tsi.Enabled = true;
            }
        }

        /// <summary>
        /// Connect selected
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void connectToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (supressEvents)
                return;

            listView_connections_DoubleClick(null, null);
        }

        /// <summary>
        /// Disconnect
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void diconnectToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (supressEvents)
                return;

            if (listView_connections.SelectedItems.Count > 0)
            {
                RasEntry re = (RasEntry)listView_connections.SelectedItems[0].Tag;

                re.Update();

                RasConnection rc = RasConnection.GetActiveConnectionByName(re.Name, CurrentUserPhoneBook.Path, StringComparison.InvariantCultureIgnoreCase);

                if (rc != null)
                {
                    rc.HangUp();
                    toolStripPPPoEConnectionStatus.Text = "PPPoE Connection Status: disconnected";
                }
                else
                {
                    foreach (ToolStripItem tsi in connectToThatACToolStripMenuItem.DropDownItems)
                    {
                        if (tsi.Text == re.Name)
                        {
                            tsi.Enabled = true;
                            break;
                        }
                    }
                    MainWindow.LogWriteLine("Selected Entry not connected", Options.Global.LogLevel.Error);
                }
            }
        }

        private void timer_Tick(object sender, EventArgs e)
        {
            if (!toolStripProgressBarConnection.Visible)
            {
                timer.Enabled = false;
            }

            toolStripProgressBarConnection.PerformStep();
        }
        #endregion

        #endregion

        #region Public Methods

        public static void StopReadingThread()
        {
            Options.Global.GlobalAbortReadingPackets = true;
            
            if (ReadPacketsThread != null)
            {
                if (ReadPacketsThread.ThreadState != System.Threading.ThreadState.Stopped
                   && ReadPacketsThread.ThreadState != System.Threading.ThreadState.StopRequested
                   && ReadPacketsThread.ThreadState != System.Threading.ThreadState.AbortRequested
                   && ReadPacketsThread.ThreadState != System.Threading.ThreadState.Aborted)
                {
                    ReadPacketsThread.Abort();
                }
            }

            if (ProcessingPacketsThread != null)
            {
                if (ProcessingPacketsThread.ThreadState != System.Threading.ThreadState.Stopped
                   && ProcessingPacketsThread.ThreadState != System.Threading.ThreadState.StopRequested
                   && ProcessingPacketsThread.ThreadState != System.Threading.ThreadState.AbortRequested
                   && ProcessingPacketsThread.ThreadState != System.Threading.ThreadState.Aborted)
                {
                    ProcessingPacketsThread.Abort();
                }
            }
        }

        #endregion

        #region Threads

        private void AsyncPacketRead()
        {
            if (MainWindow.Instance.RawEtherInstance != null)
            {
                byte[] buf = new byte[Options.Global.ReadBufferSize];
                while (true && !Options.Global.GlobalAbortReadingPackets)
                {
                    ulong readBytes = 0;

                    MainWindow.Instance.RawEtherInstance.DoRead(ref buf, out readBytes);

                    if (readBytes > 0)
                    {
                        Packets.PacketQueue.Enqueue((byte[])buf.Clone());
                    }
                }
                interruptProcessPacketAtAll = true;
            }
        }

        private void AsyncPacketProcess()
        {
            do
            {
                if (Packets.PacketQueue.PacketCount == 0)
                {
                    ContinueProcessPackets.WaitOne();
                }
                else
                {
                    byte[] packet = Packets.PacketQueue.Dequeue();

                    if (packet != null)
                    {
                        ++packetsRead;
                        bytesRead += packet.LongLength;

                        this.Invoke(InvokationUIDelegate);
                        // TODO: analysis code here code
                        if (packet[12] == 0x88
                            && packet[13] == 0x63) // PPPOE
                        {
                            lock (currentSync)
                            {
                                current = PppoeProtocolPacket.ParseFromBytes(packet);
                            }

                            CurrentPacketUpdated();
                        }
                        else if (packet[12] == 0x88
                            && packet[13] == 0x64) // PPPOE SESSION
                        {
                            lock (currentPPPoESessionPacket)
                            {
                                currentPPPoESessionPacket = ByteUtils.RemoveBytes(18, ref packet);
                            }

                            PppoeSessionCaptured();
                        }
                    }
                }
            } while (!interruptProcessPacketAtAll);
        }
        #endregion
    }
}
